﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MVCQuick.Framework.Repository;
using System.Web;
using System.IO;

namespace MVCQuick.Framework.Security
{
    public class ProfileService
    {
        IRepository repository;

        public ProfileService(IRepository repository)
        {
            this.repository = repository;
        }

        public int DeleteInactiveProfiles(string applicationName,
            System.Web.Profile.ProfileAuthenticationOption authenticationOption,
            DateTime userInactiveSinceDate)
        {
            Application application =
               new ApplicationService(repository).GetApplication(applicationName);
          
            IList<User> users = 
               repository.Query<User>()
                   .Where(x => x.Application == application)                  
                   .ToList();

            int result = 0;

            foreach (var user in users)
            {
                Profile profile = null;

                if (authenticationOption == System.Web.Profile.ProfileAuthenticationOption.Anonymous)
                {
                    if (user.LastActivityDate < userInactiveSinceDate && user.IsAnonymous)
                    {
                        profile =
                            repository.Query<Profile>()
                                .Where(x => x.UserId == user.Id)
                                .SingleOrDefault();                            
                    }
                }
                else if (authenticationOption == System.Web.Profile.ProfileAuthenticationOption.Authenticated)
                {
                    if (user.LastActivityDate < userInactiveSinceDate && !user.IsAnonymous)
                    {
                        profile =
                            repository.Query<Profile>()
                                .Where(x => x.UserId == user.Id)
                                .SingleOrDefault(); 
                    }
                }
                else
                {
                    if (user.LastActivityDate < userInactiveSinceDate)
                    {
                        profile =
                            repository.Query<Profile>()
                                .Where(x => x.UserId == user.Id)
                                .SingleOrDefault(); 
                    }
                }

                if (profile != null)
                {
                    repository.Delete(profile);
                    result++;
                }
            }

            return result;

        }

        public int DeleteProfiles(string applicationName, string[] usernames)
        {
            Application application =
               new ApplicationService(repository).GetApplication(applicationName);

            int result = 0;

            foreach (var username in usernames)
            {
                User user =
                    repository.Query<User>()
                        .Where(x => x.Application == application)
                        .Where(x => x.Username == username)
                        .SingleOrDefault();

                if (user != null)
                {
                    Profile profile =
                        repository.Query<Profile>()
                            .Where(x => x.UserId == user.Id)
                            .SingleOrDefault(); 

                    if (profile != null)
                    {
                        repository.Delete(profile);
                        result++;
                    }
                }
            }

            return result;
        }

        public IList<System.Web.Profile.ProfileInfo> FindProfiles(string applicationName,
            System.Web.Profile.ProfileAuthenticationOption authenticationOption,
            string usernameToMatch, 
            DateTime userInactiveSinceDate,
            int pageIndex, int pageSize, out int totalRecords)
        {
            IList<System.Web.Profile.ProfileInfo> profileInfos =
                new List<System.Web.Profile.ProfileInfo>();

            Application application =
               new ApplicationService(repository).GetApplication(applicationName);           

            IList<User> users =
                repository.Query<User>()
                    .Where(x => x.Application == application)
                    .Where(x => x.Username.Contains(usernameToMatch))
                    .ToList();

            foreach (var user in users)
            {
                Profile profile = null;

                if (authenticationOption == System.Web.Profile.ProfileAuthenticationOption.Anonymous)
                {
                    if (user.LastActivityDate < userInactiveSinceDate && user.IsAnonymous)
                    {
                        profile =
                            repository.Query<Profile>()
                                .Where(x => x.UserId == user.Id)
                                .SingleOrDefault(); 
                    }
                }
                else if (authenticationOption == System.Web.Profile.ProfileAuthenticationOption.Authenticated)
                {
                    if (user.LastActivityDate < userInactiveSinceDate && !user.IsAnonymous)
                    {
                        profile =
                            repository.Query<Profile>()
                                .Where(x => x.UserId == user.Id)
                                .SingleOrDefault(); 
                    }
                }
                else
                {
                    if (user.LastActivityDate < userInactiveSinceDate)
                    {
                        profile =
                            repository.Query<Profile>()
                                .Where(x => x.UserId == user.Id)
                                .SingleOrDefault(); 
                    }
                }

                if (profile != null)
                {
                    profileInfos.Add(new System.Web.Profile.ProfileInfo(
                       user.Username, this.isAnonymous(), user.LastActivityDate,
                       profile.LastUpdatedDate,
                       profile.PropertyNames.Length + profile.PropertyValuesBinary.Length + profile.PropertyValuesString.Length));

                }
            }

            totalRecords = profileInfos.Count;

            return (IList<System.Web.Profile.ProfileInfo>)
                (profileInfos.Skip(pageIndex * pageSize).Take(pageSize));
        }

        public IList<System.Web.Profile.ProfileInfo> FindProfiles(string applicationName,
            System.Web.Profile.ProfileAuthenticationOption authenticationOption,
            string usernameToMatch,
            int pageIndex, int pageSize, out int totalRecords)            
        {
            IList<System.Web.Profile.ProfileInfo> profileInfos =
                new List<System.Web.Profile.ProfileInfo>();

            Application application =
               new ApplicationService(repository).GetApplication(applicationName);

            IList<User> users =
                repository.Query<User>()
                    .Where(x => x.Application == application)
                    .Where(x => x.Username.Contains(usernameToMatch))
                    .ToList();

            foreach (var user in users)
            {
                Profile profile = null;

                if (authenticationOption == System.Web.Profile.ProfileAuthenticationOption.Anonymous)
                {
                    if (user.IsAnonymous)
                    {
                        profile =
                            repository.Query<Profile>()
                                .Where(x => x.UserId == user.Id)
                                .SingleOrDefault(); 
                    }
                }
                else if (authenticationOption == System.Web.Profile.ProfileAuthenticationOption.Authenticated)
                {
                    if (!user.IsAnonymous)
                    {
                        profile =
                            repository.Query<Profile>()
                                .Where(x => x.UserId == user.Id)
                                .SingleOrDefault(); 
                    }
                }
                else
                {
                    profile =
                        repository.Query<Profile>()
                            .Where(x => x.UserId == user.Id)
                            .SingleOrDefault(); 
                }

                if (profile != null)
                {
                    profileInfos.Add(new System.Web.Profile.ProfileInfo(
                       user.Username, this.isAnonymous(), user.LastActivityDate,
                       profile.LastUpdatedDate,
                       profile.PropertyNames.Length + profile.PropertyValuesBinary.Length + profile.PropertyValuesString.Length));

                }
            }

            totalRecords = profileInfos.Count;

            return (IList<System.Web.Profile.ProfileInfo>)
                (profileInfos.Skip(pageIndex * pageSize).Take(pageSize));
        }

        public IList<System.Web.Profile.ProfileInfo> GetAllInactiveProfiles(string applicationName,
            System.Web.Profile.ProfileAuthenticationOption authenticationOption,
            DateTime userInactiveSinceDate,
            int pageIndex, int pageSize, out int totalRecords) 
        {
            IList<System.Web.Profile.ProfileInfo> profileInfos =
                new List<System.Web.Profile.ProfileInfo>();

            Application application =
               new ApplicationService(repository).GetApplication(applicationName);

            IList<User> users =
                repository.Query<User>()
                    .Where(x => x.Application == application)                   
                    .ToList();

            foreach (var user in users)
            {
                Profile profile = null;

                if (authenticationOption == System.Web.Profile.ProfileAuthenticationOption.Anonymous)
                {
                    if (user.LastActivityDate < userInactiveSinceDate && user.IsAnonymous)
                    {
                        profile =
                            repository.Query<Profile>()
                                .Where(x => x.UserId == user.Id)
                                .SingleOrDefault(); 
                    }
                }
                else if (authenticationOption == System.Web.Profile.ProfileAuthenticationOption.Authenticated)
                {
                    if (user.LastActivityDate < userInactiveSinceDate && !user.IsAnonymous)
                    {
                        profile =
                           repository.Query<Profile>()
                               .Where(x => x.UserId == user.Id)
                               .SingleOrDefault(); 
                    }
                }
                else
                {
                    if (user.LastActivityDate < userInactiveSinceDate)
                    {
                        profile =
                            repository.Query<Profile>()
                                .Where(x => x.UserId == user.Id)
                                .SingleOrDefault(); 
                    }
                }

                if (profile != null)
                {
                    profileInfos.Add(new System.Web.Profile.ProfileInfo(
                       user.Username, this.isAnonymous(), user.LastActivityDate,
                       profile.LastUpdatedDate,
                       profile.PropertyNames.Length + profile.PropertyValuesBinary.Length + profile.PropertyValuesString.Length));

                }
            }

            totalRecords = profileInfos.Count;

            return (IList<System.Web.Profile.ProfileInfo>)
                (profileInfos.Skip(pageIndex * pageSize).Take(pageSize));
        }

        public IList<System.Web.Profile.ProfileInfo> GetAllProfiles(string applicationName,
           System.Web.Profile.ProfileAuthenticationOption authenticationOption,
            int pageIndex, int pageSize, out int totalRecords) 
        {
            IList<System.Web.Profile.ProfileInfo> profileInfos =
                 new List<System.Web.Profile.ProfileInfo>();

            Application application =
               new ApplicationService(repository).GetApplication(applicationName);

            IList<User> users =
                repository.Query<User>()
                    .Where(x => x.Application == application)
                    .ToList();

            foreach (var user in users)
            {
                Profile profile = null;

                if (authenticationOption == System.Web.Profile.ProfileAuthenticationOption.Anonymous)
                {
                    if (user.IsAnonymous)
                    {
                        profile =
                           repository.Query<Profile>()
                               .Where(x => x.UserId == user.Id)
                               .SingleOrDefault(); 
                    }
                }
                else if (authenticationOption == System.Web.Profile.ProfileAuthenticationOption.Authenticated)
                {
                    if (!user.IsAnonymous)
                    {
                        profile =
                           repository.Query<Profile>()
                               .Where(x => x.UserId == user.Id)
                               .SingleOrDefault(); 
                    }
                }
                else
                {
                    profile =
                        repository.Query<Profile>()
                            .Where(x => x.UserId == user.Id)
                            .SingleOrDefault(); 
                }

                if (profile != null)
                {
                    profileInfos.Add(new System.Web.Profile.ProfileInfo(
                       user.Username, this.isAnonymous(), user.LastActivityDate,
                       profile.LastUpdatedDate,
                       profile.PropertyNames.Length + profile.PropertyValuesBinary.Length + profile.PropertyValuesString.Length));

                }
            }

            totalRecords = profileInfos.Count;

            return (IList<System.Web.Profile.ProfileInfo>)
                (profileInfos.Skip(pageIndex * pageSize).Take(pageSize));
        }

        public int GetNumberOfInactiveProfiles(string applicationName,
            System.Web.Profile.ProfileAuthenticationOption authenticationOption,
            DateTime userInactiveSinceDate)
        {

            System.Web.Profile.ProfileInfoCollection profileInfos =
                new System.Web.Profile.ProfileInfoCollection();

            Application application =
               new ApplicationService(repository).GetApplication(applicationName);

            IList<User> users =
                repository.Query<User>()
                    .Where(x => x.Application == application)
                    .ToList();

            foreach (var user in users)
            {
                Profile profile = null;

                if (authenticationOption == System.Web.Profile.ProfileAuthenticationOption.Anonymous)
                {
                    if (user.LastActivityDate < userInactiveSinceDate && user.IsAnonymous)
                    {
                        profile =
                            repository.Query<Profile>()
                                .Where(x => x.UserId == user.Id)
                                .SingleOrDefault(); 
                    }
                }
                else if (authenticationOption == System.Web.Profile.ProfileAuthenticationOption.Authenticated)
                {
                    if (user.LastActivityDate < userInactiveSinceDate && !user.IsAnonymous)
                    {
                        profile =
                            repository.Query<Profile>()
                                .Where(x => x.UserId == user.Id)
                                .SingleOrDefault(); 
                    }
                }
                else
                {
                    if (user.LastActivityDate < userInactiveSinceDate)
                    {
                        profile =
                            repository.Query<Profile>()
                                .Where(x => x.UserId == user.Id)
                                .SingleOrDefault(); 
                    }
                }

                if (profile != null)
                {
                    profileInfos.Add(new System.Web.Profile.ProfileInfo(
                       user.Username, this.isAnonymous(), user.LastActivityDate,
                       profile.LastUpdatedDate,
                       profile.PropertyNames.Length + profile.PropertyValuesBinary.Length + profile.PropertyValuesString.Length));

                }
            }

            return profileInfos.Count;
        }

        public int GetProperties(string applicationName, string username,
            System.Configuration.SettingsPropertyValueCollection svc,
            DateTime currentTimeUtc)
        {
            Application application =
                new ApplicationService(repository).GetApplication(applicationName);

            User user =
                repository.Query<User>()
                    .Where(x => x.Application == application)
                    .Where(x => x.Username == username)
                    .SingleOrDefault();

            if (user == null)
            {
                return 1;
            }

            Profile dbProperties =
                repository.Query<Profile>()
                    .Where(x => x.UserId == user.Id)
                    .SingleOrDefault();            

            if (dbProperties == null)
            {
                return 1;
            }

            string[] names = null;
            string values = null;

            names = dbProperties.PropertyNames.Split(':');
            values = dbProperties.PropertyValuesString;

            if (names != null && names.Length > 0)
            {
                ParseDataFromDB(names, values, dbProperties.PropertyValuesBinary, svc);
            }

            return 0;
        }

        public int SetProperties(string applicationName, string username,
            System.Configuration.SettingsPropertyValueCollection properties,
            bool isAuthenticated, DateTime currentTimeUtc)
        {
            Application application =
                new ApplicationService(repository).GetApplication(applicationName);

            User user =
                repository.Query<User>()
                    .Where(x => x.Application == application)
                    .Where(x => x.Username == username)
                    .SingleOrDefault();

            if (user == null)
            {
                return 1;
            }

            string allNames = string.Empty;
            string allValues = string.Empty;
            byte[] buf = null;

            PrepareDataForSaving(ref allNames, ref allValues, ref buf, true, properties, isAuthenticated);

            if (allNames.Length == 0)
            {
                return 1;
            }

            Profile profile = new Profile();
            profile.UserId = user.Id;
            profile.PropertyNames = allNames;
            profile.PropertyValuesBinary = buf;
            profile.PropertyValuesString = allValues;
            profile.LastUpdatedDate = currentTimeUtc;

            Profile dbProperties =
                repository.Query<Profile>()
                    .Where(x => x.UserId == user.Id)
                    .SingleOrDefault();

            if (dbProperties == null)
                repository.Save(profile);
            else
                repository.Update(profile);


            return 0;
        }

        private bool isAnonymous()
        {
            HttpContext current = HttpContext.Current;

            if (current != null)
            {
                if (current.Request.IsAuthenticated)
                {
                    return false;
                }
            }
            return true;
        }

        private void ParseDataFromDB(string[] names, string values, byte[] buf,
            System.Configuration.SettingsPropertyValueCollection properties)
        {
            if (((names != null) && (values != null)) && ((buf != null) && (properties != null)))
            {
                try
                {
                    for (int i = 0; i < (names.Length / 4); i++)
                    {
                        string str = names[i * 4];
                        System.Configuration.SettingsPropertyValue value2 = properties[str];
                        if (value2 != null)
                        {
                            int startIndex = int.Parse(names[(i * 4) + 2], System.Globalization.CultureInfo.InvariantCulture);
                            int length = int.Parse(names[(i * 4) + 3], System.Globalization.CultureInfo.InvariantCulture);
                            if ((length == -1) && !value2.Property.PropertyType.IsValueType)
                            {
                                value2.PropertyValue = null;
                                value2.IsDirty = false;
                                value2.Deserialized = true;
                            }
                            if (((names[(i * 4) + 1] == "S") && (startIndex >= 0)) && ((length > 0) && (values.Length >= (startIndex + length))))
                            {
                                value2.SerializedValue = values.Substring(startIndex, length);
                            }
                            if (((names[(i * 4) + 1] == "B") && (startIndex >= 0)) && ((length > 0) && (buf.Length >= (startIndex + length))))
                            {
                                byte[] dst = new byte[length];
                                Buffer.BlockCopy(buf, startIndex, dst, 0, length);
                                value2.SerializedValue = dst;
                            }
                        }
                    }
                }
                catch
                {
                }
            }
        }

        private void PrepareDataForSaving(ref string allNames, ref string allValues, ref byte[] buf,
            bool binarySupported, System.Configuration.SettingsPropertyValueCollection properties,
            bool userIsAuthenticated)
        {
            StringBuilder builder = new StringBuilder();
            StringBuilder builder2 = new StringBuilder();
            MemoryStream stream = binarySupported ? new MemoryStream() : null;
            try
            {
                try
                {
                    bool flag = false;
                    foreach (System.Configuration.SettingsPropertyValue value2 in properties)
                    {
                        if (value2.IsDirty && (userIsAuthenticated || ((bool)value2.Property.Attributes["AllowAnonymous"])))
                        {
                            flag = true;
                            break;
                        }
                    }
                    if (!flag)
                    {
                        return;
                    }
                    foreach (System.Configuration.SettingsPropertyValue value3 in properties)
                    {
                        if ((!userIsAuthenticated && !((bool)value3.Property.Attributes["AllowAnonymous"])) || (!value3.IsDirty && value3.UsingDefaultValue))
                        {
                            continue;
                        }
                        int length = 0;
                        int position = 0;
                        string str = null;
                        if (value3.Deserialized && (value3.PropertyValue == null))
                        {
                            length = -1;
                        }
                        else
                        {
                            object serializedValue = value3.SerializedValue;
                            if (serializedValue == null)
                            {
                                length = -1;
                            }
                            else
                            {
                                if (!(serializedValue is string) && !binarySupported)
                                {
                                    serializedValue = Convert.ToBase64String((byte[])serializedValue);
                                }
                                if (serializedValue is string)
                                {
                                    str = (string)serializedValue;
                                    length = str.Length;
                                    position = builder2.Length;
                                }
                                else
                                {
                                    byte[] buffer = (byte[])serializedValue;
                                    position = (int)stream.Position;
                                    stream.Write(buffer, 0, buffer.Length);
                                    stream.Position = position + buffer.Length;
                                    length = buffer.Length;
                                }
                            }
                        }
                        builder.Append(value3.Name + ":" + ((str != null) ? "S" : "B") + ":" + position.ToString(System.Globalization.CultureInfo.InvariantCulture) + ":" + length.ToString(System.Globalization.CultureInfo.InvariantCulture) + ":");
                        if (str != null)
                        {
                            builder2.Append(str);
                        }
                    }
                    if (binarySupported)
                    {
                        buf = stream.ToArray();
                    }
                }
                finally
                {
                    if (stream != null)
                    {
                        stream.Close();
                    }
                }
            }
            catch
            {
                throw;
            }
            allNames = builder.ToString();
            allValues = builder2.ToString();
        }
    }
}
